package io.github.ali.commons.lock.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.github.ali.commons.util.Asserts;

/**
 * 本地锁
 * @author bailey
 * @date 2021年10月29日
 */
public class NativeLocker extends AbstractLocker{
	private static Logger LOGGER = LoggerFactory.getLogger(NativeLocker.class);
	private Map<String, LockerValue> storage = new HashMap<>();
	private boolean reentryAble;
	
	public NativeLocker() {
		this.reentryAble = false;
	}
	public NativeLocker(boolean reentryAble) {
		this.reentryAble = reentryAble;
	}
	
	@Override
	protected void doLock(String key, String value, long expiring) throws Exception {
		synchronized (key.intern()) {
			LockerValue lockerValue = storage.get(key);
			if (lockerValue != null) {
				//可重入;超时时间以最外层锁传入时间为准
				if (reentryAble && lockerValue.holderThread == Thread.currentThread()) {
					lockerValue.reentry(value);
					return;
				}
				Asserts.checkRev(lockerValue.isAlive(), "storage has already contained key : " + key);
				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("the key '{}' with value '{}' has already expired , is removed by value '{}'",key,lockerValue.value,value);
				}
				storage.remove(key);
			}
			storage.put(key, new LockerValue(value,expiring < 1L ? 0L : (System.currentTimeMillis() + expiring * 1000),Thread.currentThread()));
		}
	}

	@Override
	protected void doUnlock(String key, String value) throws Exception {
		LockerValue lockerValue = storage.get(key);
		Asserts.check(lockerValue != null, "No exist key : " + key);
		Asserts.check(lockerValue.isLegalValue(value), "No matched value found by " + value + " for key : "+key);
		if(lockerValue.clear(value)) {
			storage.remove(key);
		}
	}
	
	public void clearExpired() {
		List<String> deadKey = new ArrayList<>();
		for (Map.Entry<String, LockerValue> entry : storage.entrySet()) {
			if (entry.getValue().isAlive()) {
				continue;
			}
			deadKey.add(entry.getKey());
		}
		deadKey.stream().forEach(storage::remove);
	}

	public boolean isReentryAble() {
		return reentryAble;
	}

	public void setReentryAble(boolean reentryAble) {
		this.reentryAble = reentryAble;
	}
	private class LockerValue{
		//首次加锁时的值
		String value;
		//重入加锁时的值
		List<String> values;
		//首次加锁的有效时间
		long deadlineInMillis;
		Thread holderThread;
		LockerValue(String value,long deadlineInMillis,Thread holderThread){
			this.value = value;
			this.deadlineInMillis = deadlineInMillis;
			this.holderThread=holderThread;
		};
		boolean isAlive() {
			return deadlineInMillis < 1L || deadlineInMillis >= System.currentTimeMillis();
		}
		boolean isLegalValue(String value) {
			return this.value.equals(value) || (values != null && values.contains(value));
		}
		boolean clear(String value) {
			if (this.value.equals(value)) {
				return true;
			}
			if (values != null) {
				values.remove(value);
			}
			return false;
		}
		void reentry(String value) {
			if (!this.value.equals(value)) {
				if (values == null) {
					values = new LinkedList<>();
				}
				values.add(value);
			}
		}
	}
}
